// $ 函数

// # --函数的类型签名  函数声明
console.log('# --函数的类型签名  函数声明');
function foo(name: string): number {
  return name.length;
}

console.log(foo('lys'));

// # -- 函数表达式
console.log('# -- 函数表达式');
// 方式一
const foo1 = (name: string): number => {
  return name.length
}

// 方式二 :方式二的声明方式中，你会发现函数类型声明混合箭头函数声明时，代码的可读性会非常差。
const foo2: (name: string) => number = (name) => {
  return name.length
}

//一般不推荐这么使用，要么直接在函数中进行参数和返回值的类型声明，要么使用类型别名将函数声明抽离出来：
type FuncFoo = (name: string) => number
const foo2_fix: FuncFoo = (name) => {
  return name.length
}

// # --箭头函数
console.log('# --箭头函数');

const foo3 = (name: string) => { return name.length }

// 如果只是为了描述这个函数的类型结构，我们甚至可以使用 interface 来进行函数声明：
interface FuncFooStruct {
  (name: string): number
}

const foo3_1: FuncFooStruct = (name) => { return name.length }

console.log(foo3_1('FuncFooStruct'));

//# ------void 类型
// 没有调用 return 语句
function foo4(): void { }

// 调用了 return 语句，但没有返回值 ;
function bar(): void {
  return;
}
// 其实更好的方式是使用 undefined
function bar2(): undefined {
  return;
}


// #--- 可选参数与 rest 参数
console.log(' #--- 可选参数与 rest 参数');

// 在函数逻辑中注入可选参数默认值
function foo5_1(name: string, age?: number): number {
  const inputAge = age || 18;// 或使用 age ?? 18
  return name.length + inputAge;
}
console.log(foo5_1('liuyisong', 21));

// 直接为可选参数声明默认值
function foo5_2(name: string, age: number = 0): number {
  const inputAge = age;
  return name.length + inputAge
}
console.log(foo5_2('123.123.123.123'));

function foo5_3(arg1: string, ...res: any[]): number {
  return 1
}

function foo5_4(arg1: string, ...rest: [number, boolean]) { }

foo5_4("linbudu", 18, true)

// # -----重载
function foo6_1(foo: number, bar?: boolean): string | number {
  if (bar) {
    return String(foo)
  } else {
    return foo * 599
  }
}

const res6_1 = foo6_1(123)

// # ---函数重载签名
function foo6_2(foo: number, bar: true): string;
function foo6_2(foo: number, bar?: false): number;
function foo6_2(foo: number, bar?: boolean): string | number {
  if (bar) {
    return String(foo)
  } else {
    return foo * 599
  }
};
const res1 = foo6_2(599)
const res2 = foo6_2(599, true);
const res3 = foo6_2(599, false);

console.log('res__', res1, res2, res3);

// # ----异步函数、Generator 函数等类型签名
async function asyncFunc(): Promise<void> { }

function* genFunc(): Iterable<void> { }

async function* asyncGenFunc(): AsyncIterable<void> { }

// # ---- Class

// # ---类与类成员的类型签名
// 类声明
class Foo7_1 {
  prop: string;

  constructor(inputProp: string) {
    this.prop = inputProp
  }

  print(addon: string): void {
    console.log(`${this.prop} and ${addon}`)

  }

  get propA(): string {
    return `${this.prop}+A`;
  }
  // setter 方法不允许进行返回值的类型标注
  set propA(value: string) {
    this.prop = `${value}+A`
  }
}

console.log(new Foo7_1('lys'));


// 类表达式
const Foo7_2 = class {
  prop: string;

  constructor(inputProp: string) {
    this.prop = inputProp;
  }

  print(addon: string): void {
    console.log(`${this.prop} and ${addon}`)
  }

  // ...
}

// # ----修饰符
// public / private / protected / readonly。除 readonly 以外，其他三位都属于访问性修饰符，而 readonly 属于操作性修饰符（就和 interface 中的 readonly 意义一致）

class Foo7_3 {
  private prop: string;

  constructor(inputProp: string) {
    this.prop = inputProp;
  }

  protected print(addon: string): void {
    console.log(`${this.prop} and ${addon}`)

  }

  public get propA(): string {
    return `${this.prop}+A`;
  }
  public set propA(value: string) {
    this.propA = `${value}+A`
  }
}

// 我们可以在构造函数中对参数应用访问性修饰符

class Foo7_4 {
  constructor(public arg1: string, private arg2: boolean) { }
}

new Foo7_4("linbudu", true)

//# -----静态成员

class Foo7_5 {
  static staticHandler() { }

  public instanceHandler() { }
}

class Utils {
  public static identifier = "linbudu";

  public static makeUHappy() {
    Utils.studyWithU();
    // ...
  }

  public static studyWithU() {
    console.log('studyWithU');
  }
}

Utils.makeUHappy();

// # --继承、实现、抽象类
console.log(' # --继承、实现、抽象类');
class Base {
  print(num?: number) {
    console.log(num);
  }
}

class Derived extends Base {
  //TypeScript 4.3 新增了 override 关键字,来确保派生类（子类）尝试覆盖的方法一定在基类（父类）中存在定义
  override print(num?: number) {
    super.print(num)
    console.log((num || 0) + 123);
  }
}

new Derived().print(1)

abstract class AbsFoo {
  abstract absProp: string;
  abstract get absGetter(): string;
  abstract absMethod(name: string): string;
}

class Foo7_6 implements AbsFoo {
  absProp: string = 'lys'

  get absGetter() {
    return 'lys'
  }

  absMethod(name: string): string {
    return name;
  }
}

// interface 不仅可以声明函数结构，也可以声明类的结构
interface FooStruct {
  absProp: string;
  get absGetter(): string;
  absMethod(input: string): string
}

class Foo7_7 implements FooStruct {
  absProp: string = 'lys'
  get absGetter() {
    return "linbudu"
  }
  absMethod(name: string) {
    return name
  }
}

// # 私有构造函数

class Foo8 {
  private constructor() { }
}
// new Foo8()


// 比如像我一样把类作为 utils 方法时，此时 Utils 类内部全部都是静态成员，我们也并不希望真的有人去实例化这个类。此时就可以使用私有构造函数来阻止它被错误地实例化：
class Utils8 {
  public static identifier = "linbudu";

  private constructor() { }

  public static makeUHappy() {
  }
}
console.log(Utils8);

// new Utils8()
enum Items {
  Foo = "bar",
  Bar = "BarValue",
  Baz = "BazValue"
}
enum LoginType {
  WeChat,
  TaoBao,
  TikTok,
}


// # SOLID 原则

// O，开放封闭原则，一个类应该是可扩展但不可修改的。即假设我们的业务中支持通过微信、支付宝登录，原本在一个 login 方法中进行 if else 判断，假设后面又新增了抖音登录、美团登录，难道要再加 else if 分支（或 switch case）吗？
class Login {
  public static handler(type: LoginType) {
    if (type === LoginType.WeChat) { }
    else if (type === LoginType.TikTok) { }
    else if (type === LoginType.TaoBao) { }
    else {
      throw new Error("Invalid Login Type!")
    }
  }
}

// 当然不，基于开放封闭原则，我们应当将登录的基础逻辑抽离出来，不同的登录方式通过扩展这个基础类来实现自己的特殊逻辑。

abstract class LoginHandler9 {
  abstract handler(): void
}

class WeChatLoginHandler implements LoginHandler9 {
  handler() {
    console.log('WeChatLoginHandler__login');

  }
}

class TaoBaoLoginHandler implements LoginHandler9 {
  handler() {
    console.log('TaoBaoLoginHandler__login');
  }
}

class TikTokLoginHandler implements LoginHandler9 {
  handler() {
    console.log('TikTokLoginHandler__login');
  }
}

class Login9 {
  public static handlerMap: Record<LoginType, LoginHandler9> = {
    [LoginType.TaoBao]: new TaoBaoLoginHandler(),
    [LoginType.TikTok]: new TikTokLoginHandler(),
    [LoginType.WeChat]: new WeChatLoginHandler(),
  }

  public static handler(type: LoginType) {
    Login9.handlerMap[type].handler();
  }
}

console.log(Login9.handlerMap);

Login9.handler(1)

